using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Permissions;
using System.Security.Principal;
using OracleInternal.Common;
using OracleInternal.Secure.Network;

namespace OracleInternal.Network
{
	internal class Ano
	{
		internal enum ServicesSupported
		{
			NONE,
			AUTHENTICATION,
			ENCRYPTION,
			DATAINTEGRITY,
			SUPERVISOR
		}

		internal const ushort NAUZTK5_ADDRTYPE_INET = 2;

		internal const ushort NAUZTK5_ADDRTYPE_CHAOS = 5;

		internal const ushort NAUZTK5_ADDRTYPE_XNS = 6;

		internal const ushort NAUZTK5_ADDRTYPE_ISO = 7;

		internal const ushort NAUZTK5_ADDRTYPE_DDP = 16;

		internal const ushort NAUZTK5_ADDRTYPE_INET6 = 24;

		internal const int NSINAWANTED = 1;

		internal const int NSINAINTCHG = 2;

		internal const int NSINADISABLEFORCONNECTION = 4;

		internal const int NSINANOSERVICES = 8;

		internal const int NSINAREQUIRED = 16;

		internal const int NSINAAUTHWANTED = 32;

		internal const int NUM_SERVICES = 5;

		internal SessionContext m_sessionContext;

		internal AnoCommunication m_anoComm;

		internal int m_naFlags = 1;

		internal byte[] clientPK;

		internal byte[] iv;

		internal byte[] skey;

		internal bool foldedinkey;

		internal DataIntegrityAlgorithm dataIntegrityAlg;

		internal AnoService[] m_listOfServices = new AnoService[5];

		public void Initialize(SessionContext sessCtx)
		{
			m_sessionContext = sessCtx;
			m_sessionContext.m_ano = this;
			m_listOfServices = new AnoService[5];
			m_anoComm = new AnoCommunication(sessCtx);
			for (ServicesSupported servicesSupported = ServicesSupported.AUTHENTICATION; servicesSupported <= ServicesSupported.SUPERVISOR; servicesSupported++)
			{
				AnoService anoService = null;
				try
				{
					switch (servicesSupported)
					{
					case ServicesSupported.SUPERVISOR:
						anoService = new SupervisorService();
						break;
					case ServicesSupported.AUTHENTICATION:
						anoService = new AuthenticationService();
						break;
					case ServicesSupported.ENCRYPTION:
						anoService = new EncryptionService();
						break;
					case ServicesSupported.DATAINTEGRITY:
						anoService = new DataIntegrityService();
						break;
					}
				}
				catch (Exception)
				{
					throw new NetworkException(-6308);
				}
				m_naFlags |= anoService.Initialize(sessCtx);
				m_listOfServices[(int)servicesSupported] = anoService;
			}
			if ((m_naFlags & 0x10) > 0 && (m_naFlags & 8) > 0)
			{
				m_naFlags &= -17;
			}
		}

		[SecurityPermission(SecurityAction.Assert, ControlPrincipal = true)]
		internal void StartNegotiation()
		{
			int num = 0;
			for (int i = 1; i < 5; i++)
			{
				num += m_listOfServices[i].NumberOfBytesNeeded();
			}
			int pktLength = 13 + num;
			SendANOHeader(pktLength, 4, 0);
			m_listOfServices[4].SendServiceData();
			m_listOfServices[1].SendServiceData();
			m_listOfServices[2].SendServiceData();
			m_listOfServices[3].SendServiceData();
			m_anoComm.FlushData();
			int[] array = ReceiveANOHeader();
			for (int j = 0; j < array[2]; j++)
			{
				int[] array2 = AnoService.ReceiveHeader(m_anoComm);
				if (array2[2] != 0)
				{
					throw new NetworkException(array2[2]);
				}
				m_listOfServices[array2[0]].ReceiveSelection(array2[1]);
			}
			for (int k = 1; k < 5; k++)
			{
				m_listOfServices[k].ActivateAlgorithm();
			}
			AuthenticationService authenticationService = (AuthenticationService)m_listOfServices[1];
			bool flag = false;
			bool flag2 = false;
			if (authenticationService.m_authenticationActivated)
			{
				if (authenticationService.m_authenticationService == "KERBEROS5")
				{
					flag = true;
				}
				else if (authenticationService.m_authenticationService == "NTS")
				{
					flag2 = true;
				}
			}
			int num2 = 0;
			int num3 = 0;
			if (clientPK != null)
			{
				num2 += 12 + clientPK.Length;
				num3++;
			}
			if (flag)
			{
				num2 += 37;
				num3++;
			}
			else if (flag2)
			{
				num2 += 130;
				num3++;
			}
			if (num2 > 0)
			{
				num2 += 13;
				SendANOHeader(num2, num3, 0);
				if (clientPK != null)
				{
					m_listOfServices[3].SendHeader(1);
					m_anoComm.SendRaw(clientPK);
				}
				if (flag)
				{
					authenticationService.SendHeader(4);
					m_anoComm.SendVersion();
					m_anoComm.SendUB4(9L);
					m_anoComm.SendUB4(2L);
					m_anoComm.SendUB1(1);
				}
				else if (flag2)
				{
					new NegotiateStream(new AnoStream(m_sessionContext.m_transportAdapter, m_sessionContext), leaveInnerStreamOpen: true).AuthenticateAsClient(CredentialCache.DefaultNetworkCredentials, "", ProtectionLevel.None, TokenImpersonationLevel.Identification);
					return;
				}
				m_anoComm.FlushData();
				if (flag)
				{
					SqlNetOraConfig sNOConfig = m_sessionContext.m_conops.SNOConfig;
					string kRB5Conf = sNOConfig["sqlnet.kerberos5_conf"];
					string kRB5CCName = sNOConfig["sqlnet.kerberos5_cc_name"];
					KerberosHandshake(authenticationService, kRB5Conf, kRB5CCName);
				}
			}
		}

		private void KerberosHandshake(AuthenticationService AS, string KRB5Conf, string KRB5CCName)
		{
			int[] array = ReceiveANOHeader();
			for (int i = 0; i < array[2]; i++)
			{
				int[] array2 = AnoService.ReceiveHeader(m_anoComm);
				if (array2[2] != 0)
				{
					throw new NetworkException(array2[2]);
				}
			}
			string text = m_anoComm.ReceiveString();
			string text2 = m_anoComm.ReceiveString();
			if (string.IsNullOrEmpty(text))
			{
				throw new NetworkException(-6330, "Service Name not received");
			}
			if (string.IsNullOrEmpty(text2))
			{
				throw new NetworkException(-6330, "Server hostname not received");
			}
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.Ano, OracleTraceFuncName.KerberosHandshake, "SN = " + text + ". Server HOSTNAME = " + text2);
			}
			if (string.IsNullOrEmpty(KRB5Conf))
			{
				throw new NetworkException(-6330, "SQLNET.KERBEROS5_CONF missing");
			}
			if (string.IsNullOrEmpty(KRB5CCName))
			{
				throw new NetworkException(-6330, "SQLNET.KERBEROS5_CC_NAME missing");
			}
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.Ano, OracleTraceFuncName.KerberosHandshake, "SQLNET.KERBEROS5_CONF = " + KRB5Conf + ". SQLNET.KERBEROS_CC_NAME = " + KRB5CCName);
			}
			byte[] array3;
			try
			{
				Type type = Assembly.Load($"Oracle.ManagedDataAccessIOP, Version={ConfigBaseClass.m_assemblyVersion}, Culture=neutral, PublicKeyToken=89b483f429c47342").GetType("OracleInternal.Network.Kerberos.Kerberos5");
				ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
				try
				{
					object obj = constructor.Invoke(null);
					MethodInfo method = type.GetMethod("Authenticate");
					object[] parameters = new object[4]
					{
						KRB5Conf,
						KRB5CCName,
						text,
						text2
					};
					array3 = (byte[])method.Invoke(obj, parameters);
				}
				catch (Exception ex)
				{
					if (ex.InnerException != null)
					{
						throw ex.InnerException;
					}
					throw ex;
				}
			}
			catch (Exception ex2)
			{
				throw new NetworkException(-6330, ex2.Message);
			}
			IPAddress[] hostAddresses = Dns.GetHostAddresses("");
			if (hostAddresses == null)
			{
				throw new NetworkException(-6330, "Unable to resolve local hostname");
			}
			ushort num;
			if (hostAddresses[0].AddressFamily == AddressFamily.InterNetwork)
			{
				num = 2;
			}
			else
			{
				if (hostAddresses[0].AddressFamily != AddressFamily.InterNetworkV6)
				{
					throw new NetworkException(-6330, NetworkException.sprintf("Invalid Local Address Family (%d)", hostAddresses[0].AddressFamily));
				}
				num = 24;
			}
			byte[] addressBytes = hostAddresses[0].GetAddressBytes();
			int num2 = addressBytes.Length;
			SendANOHeader(array3.Length + 43 + num2, 1, 0);
			AS.SendHeader(4);
			m_anoComm.SendUB2(num);
			m_anoComm.SendUB4(num2);
			m_anoComm.SendRaw(addressBytes);
			m_anoComm.SendRaw(array3);
			m_anoComm.FlushData();
			array = ReceiveANOHeader();
			for (int j = 0; j < array[2]; j++)
			{
				int[] array4 = AnoService.ReceiveHeader(m_anoComm);
				if (array4[2] != 0)
				{
					throw new NetworkException(array4[2]);
				}
			}
			int num3 = m_anoComm.ReceivePacketHeader(2);
			m_anoComm.ReadUB1();
			num3 = m_anoComm.ReceivePacketHeader(1);
			m_anoComm.ReceiveByteArray(num3);
			SendANOHeader(25, 1, 0);
			AS.SendHeader(1);
			m_anoComm.SendPacketHeader(0, 1);
			m_anoComm.FlushData();
		}

		internal void SendANOHeader(int pktLength, int numServices, short errorFlags)
		{
			m_anoComm.WriteUB4(3735928559L);
			m_anoComm.WriteUB2(pktLength);
			m_anoComm.WriteVersion();
			m_anoComm.WriteUB2(numServices);
			m_anoComm.WriteUB1(errorFlags);
		}

		internal int[] ReceiveANOHeader()
		{
			if (m_anoComm.ReadUB4() != 3735928559u)
			{
				throw new NetworkException(2514);
			}
			return new int[4]
			{
				m_anoComm.ReadUB2(),
				(int)m_anoComm.ReadUB4(),
				m_anoComm.ReadUB2(),
				m_anoComm.ReadUB1()
			};
		}

		internal void setClientPK(byte[] clientPK)
		{
			this.clientPK = clientPK;
		}

		internal void setInitializationVector(byte[] iv)
		{
			this.iv = iv;
		}

		internal void setSessionKey(byte[] skey)
		{
			this.skey = skey;
		}

		internal byte[] getInitializationVector()
		{
			return iv;
		}

		internal byte[] getSessionKey()
		{
			return skey;
		}
	}
}
